/**
 * blacksmith/game - A game library
 */


define([
'lib/three',
'lib/cannon'
], function() {


var GAME = window.GAME = {}


GAME.PHYSICSDT = 1 / 7
GAME.ANTIALIAS = true
GAME.Camera = {
    object : null,
    target : null,
    lon : 0,
    lat : 0,
    phi : -90 * Math.PI / 180,
    theta : -90 * Math.PI / 180
}
GAME.Scene = null
GAME.SceneSize = 10000
GAME.ViewWidth = 640
GAME.ViewHeight = 480
GAME.Renderer = null
GAME.World = null
GAME.Mouse = {
    button : 0,
    worldPosition : new THREE.Vector3,
    screenPosition : new THREE.Vector2,
    overBlock : null,
    dragBlock : null,
    ray : new THREE.Ray,
    projector : new THREE.Projector,
    intersectMeshes : null
}
GAME.ActiveRootBlocks = []
GAME.AllBlocks = []
GAME.Meshes = []
GAME.EmptyCallback = function() {}
GAME.InitialDirection = new THREE.Vector3(0, 0, -1)
GAME.Gravity = 0
GAME.Z_AXIS_VEC3 = new THREE.Vector3(0, 0, 1)
GAME.time = 0
GAME.timeDelta = 0




GAME.CreateScene = function(container, sceneSize, viewWidth, viewHeight, gravity) {
    /**
     * GAME.CreateScene(container, scene_size, gravity_x, gravity_y, gravity_z)
     *
     * Initialise the scenes physical world and graphical context.
     *
     * @param container a HTMLElement
     * @param sceneSize optional defaults to 10000
     * @param gravity_x optional defaults to 0
     * @param gravity_y optional defaults to 0
     * @param gravity_z optional defaults to 0
     *
     */
    
    if (sceneSize || sceneSize === 0)
        GAME.SceneSize = sceneSize
    
    if (viewWidth || viewWidth === 0)
        GAME.ViewWidth = viewWidth
    
    if (viewHeight || viewHeight === 0)
        GAME.ViewHeight = viewHeight
    
    GAME.Gravity = gravity || 0
    
    GAME.World = new CANNON.World()
    GAME.World.gravity.set(0, GAME.Gravity, 0)
    GAME.World.broadphase = new CANNON.NaiveBroadphase()
    GAME.World.solver.iterations = 50
    GAME.World.solver.h = GAME.PHYSICSDT
    
    
    
    
    GAME.Scene = new THREE.Scene()
    
    GAME.Camera.object = new THREE.PerspectiveCamera(50, container.offsetWidth / container.offsetHeight, 1, GAME.SceneSize * 2)
    GAME.Camera.object.up.set(0, 1, 0)
    GAME.Camera.target = new THREE.Vector3(0, 0, 0)
    GAME.Camera.object.lookAt(GAME.Camera.target)
    GAME.Scene.add(GAME.Camera.object)
    
    
    
    GAME.Renderer = new THREE.WebGLRenderer({
        antialias: GAME.ANTIALIAS,
        preserveDrawingBuffer: true
    })
    GAME.Renderer.setSize(GAME.ViewWidth, GAME.ViewHeight)
    container.appendChild(GAME.Renderer.domElement)
    
    
    
    GAME.ambientLight = new THREE.AmbientLight(0x222222, 1)
    GAME.Scene.add(GAME.ambientLight)
    
    
    
    GAME.sun = new THREE.DirectionalLight(0xffffff, 10)
    GAME.sun.position.y = GAME.SceneSize * 0.55
    GAME.sun.intensity = 3
    GAME.sun.target.add(GAME.sun)
    GAME.Scene.add(GAME.sun.target)
    
    
    
    
    
    
    window.addEventListener('resize', function(/*Event*/event) {
        GAME.Camera.object.aspect = container.offsetWidth / container.offsetHeight
        GAME.Camera.object.updateProjectionMatrix()
    }, false)
    
    GAME.Renderer.domElement.addEventListener('keydown', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        GAME.onKeyDown(event.which)
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener('keyup', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        GAME.onKeyUp(event.which)
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener(/Firefox/i.test(navigator.userAgent) ? "DOMMouseScroll" : "mousewheel", function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        GAME.mousewheel_delta = /Firefox/i.test(navigator.userAgent) ? -event.detail / 3 : event.wheelDelta / 120
        if (GAME.Mouse.dragBlock != null)
            GAME.Mouse.dragBlock.onMouseWheel(GAME.mousewheel_delta)
        GAME.onMouseWheel(GAME.mousewheel_delta)
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener('mousedown', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        if (GAME.Mouse.dragBlock != null)
            GAME.Mouse.dragBlock.onMouseDown(event)
		else if (GAME.Mouse.overBlock != null)
            GAME.Mouse.overBlock.onMouseDown(event)
        GAME.Mouse.button = event.which
        GAME.onMouseDown(event)
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener('mouseup', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        GAME.Mouse.button = 0
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener('contextmenu', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        return false
    }, false)
    
    GAME.Renderer.domElement.addEventListener('mousemove', function(/*Event*/event) {
        event.preventDefault()
        event.stopPropagation()
        
        GAME.Mouse.worldPosition.x = (((document.documentElement.scrollLeft || document.body.scrollLeft) + event.clientX) / container.offsetWidth) * 2 - 1
        GAME.Mouse.worldPosition.y = -((((document.documentElement.scrollTop || document.body.scrollTop) + event.clientY) / container.offsetHeight) * 2 - 1)
        GAME.Mouse.worldPosition.z = 0.5
        
        GAME.Mouse.projector.unprojectVector(GAME.Mouse.worldPosition, GAME.Camera.object)
        
        GAME.Mouse.ray.direction = GAME.Mouse.worldPosition.subSelf(GAME.Camera.object.position).normalize()
        
        GAME.Mouse.intersectMeshes = GAME.Mouse.ray.intersectObjects(GAME.Meshes)
        
        if (GAME.Mouse.dragBlock != null) {
            
            GAME.mousemove_dx = GAME.Camera.object.position.x - GAME.Mouse.dragBlock.mesh.position.x,
            GAME.mousemove_dy = GAME.Camera.object.position.y - GAME.Mouse.dragBlock.mesh.position.y,
            GAME.mousemove_dz = GAME.Camera.object.position.z - GAME.Mouse.dragBlock.mesh.position.z
            GAME.Mouse.distance = Math.sqrt(
                (GAME.mousemove_dx * GAME.mousemove_dx) +
                (GAME.mousemove_dy * GAME.mousemove_dy) +
                (GAME.mousemove_dz * GAME.mousemove_dz)
            )
            GAME.Mouse.screenPosition.x = GAME.Camera.object.position.x + GAME.Mouse.distance * GAME.Mouse.ray.direction.x
            GAME.Mouse.screenPosition.y = GAME.Camera.object.position.z + GAME.Mouse.distance * GAME.Mouse.ray.direction.z
            
            if (GAME.Mouse.dragBlock.parent != null)
                // dragBlock currently attached to a parent, adjust position relative to parent
                GAME.Mouse.dragBlock.setPosition(
                    GAME.Mouse.screenPosition.x - GAME.Mouse.dragBlock.parent.mesh.position.x,
                    GAME.Mouse.dragBlock.parent.mesh.position.y,
                    GAME.Mouse.screenPosition.y - GAME.Mouse.dragBlock.parent.mesh.position.z
                )
                
            else
                // adjust position relative to world
                GAME.Mouse.dragBlock.setPosition(
                    GAME.Mouse.screenPosition.x,
                    GAME.Mouse.dragBlock.mesh.position.y,
                    GAME.Mouse.screenPosition.y
                );
                
            GAME.Mouse.dragBlock.onMouseDrag()
            
        } else {
            
            if (GAME.Mouse.intersectMeshes.length > 0) {
            	
                GAME.mousemove_block = GAME.meshToBlock(GAME.Mouse.intersectMeshes[0].object)
                
                if (GAME.Mouse.overBlock !== GAME.mousemove_block) {
                	
                    if (GAME.Mouse.overBlock !== null)
                        GAME.Mouse.overBlock.onMouseExit()
                        
                    GAME.mousemove_block.onMouseEnter()
                    
                }
                
                GAME.Mouse.overBlock = GAME.mousemove_block
                
            } else if (GAME.Mouse.overBlock !== null) {
                
                GAME.Mouse.overBlock.onMouseExit()
                GAME.Mouse.overBlock = null
                
            } else
                GAME.Mouse.overBlock = null
        }
        
        GAME.onMouseMove(event)
        return false
        
    }, false)
    
    
    return GAME
    
}



GAME.play = function(callback) {
    /**
     *>GAME.play()
     *
     * Start the physics and rendering updates.
     *
     */
    
    var i, t, b, lastTime = Date.now()
    
    if (typeof callback !== 'function')
        callback = function(){}
    
    var frame = function(time) {
        
        GAME.time = time
        GAME.timeDelta = time - lastTime
        lastTime = time
        
        requestAnimationFrame(frame)
        GAME.World.step(GAME.PHYSICSDT)
        GAME.Renderer.render(GAME.Scene, GAME.Camera.object)
        
        i = GAME.ActiveRootBlocks.length
        while (i--) {
            
            b = GAME.ActiveRootBlocks[i]
            
            b.mesh.position.x = b.body.position.x
            b.mesh.position.y = b.body.position.y
            b.mesh.position.z = b.body.position.z
            b.mesh.quaternion.x = b.body.quaternion.x
            b.mesh.quaternion.y = b.body.quaternion.y
            b.mesh.quaternion.z = b.body.quaternion.z
            b.mesh.quaternion.w = b.body.quaternion.w
            
            b.update()
            
        }
        
        callback()
        
    }
    
    frame(Date.now())
}


/**
 * Mouse has moved.
 * 
 * @param {event} event
 */
GAME.onMouseMove = GAME.EmptyCallback


/**
 * Mouse button is pressed.
 * 
 * @param {event} event
 */
GAME.onMouseDown = GAME.EmptyCallback


/**
 * Mouse wheel is moved.
 * 
 * @param {interger} delta
 */
GAME.onMouseWheel = GAME.EmptyCallback


/**
 * Key is pressed.
 * 
 * @param {interger} key
 */
GAME.onKeyDown = GAME.EmptyCallback


/**
 * Key is released.
 * 
 * @param {interger} key
 */
GAME.onKeyUp = GAME.EmptyCallback


/**
 * Turn shadows on or off.
 * 
 * @param {boolean} enable
 */
GAME.shadows = function(enable) {
    
	if (!enable) {
        GAME.Renderer.shadowMapEnabled = false
        return
    }
    
    GAME.Renderer.shadowMapEnabled = true
    GAME.Renderer.shadowMapSoft = true
    GAME.sun.castShadow = true
    GAME.sun.shadowDarkness = 0.5
    GAME.sun.shadowMapWidth = 2048
    GAME.sun.shadowMapHeight = 2048
    GAME.sun.shadowBias = -0.003
    GAME.sun.shadowCameraFar = GAME.SceneSize
    GAME.sun.shadowCameraLeft = -GAME.SceneSize
    GAME.sun.shadowCameraRight = GAME.SceneSize
    GAME.sun.shadowCameraTop = GAME.SceneSize * 0.5
    GAME.sun.shadowCameraBottom = -GAME.SceneSize * 0.5
    
}


GAME.updateCamera = function() {
    /**
     * Update camera dependants
     */
    
    GAME.Camera.target.x = GAME.Camera.object.position.x + 100 * Math.sin(GAME.Camera.phi) * Math.cos(GAME.Camera.theta)
    GAME.Camera.target.y = GAME.Camera.object.position.y + 100 * Math.cos(GAME.Camera.phi)
    GAME.Camera.target.z = GAME.Camera.object.position.z + 100 * Math.sin(GAME.Camera.phi) * Math.sin(GAME.Camera.theta)
    
    GAME.Camera.object.lookAt(GAME.Camera.target)
    
    GAME.Mouse.ray.origin.x = GAME.Camera.object.position.x
    GAME.Mouse.ray.origin.y = GAME.Camera.object.position.y
    GAME.Mouse.ray.origin.z = GAME.Camera.object.position.z
    
}



var Intersects = {ray : new THREE.Ray, blocks : [], i : 0}

GAME.intersects = function(ox, oy, oz, dx, dy, dz) {
    
    Intersects.ray.origin.x = ox
    Intersects.ray.origin.y = oy
    Intersects.ray.origin.z = oz
    Intersects.ray.direction.x = dx
    Intersects.ray.direction.y = dy
    Intersects.ray.direction.z = dz
    
    Intersects.blocks = Intersects.ray.intersectObjects(GAME.Meshes)
    Intersects.i = Intersects.blocks.length
    
    while (Intersects.i--)
        Intersects.blocks[Intersects.i] = GAME.meshToBlock(Intersects.blocks[Intersects.i].object)
    
    return Intersects.blocks
    
}



GAME.shiftX = function(delta) {
    /**
     * Shift the camera and target along the X axis.
     */
    
    GAME.Camera.object.position.x += delta
    GAME.Camera.target.x += delta
    GAME.sun.target.position.x = GAME.Camera.target.x
    
    GAME.updateCamera()
    
}



GAME.shiftY = function(delta) {
    /**
     * Shift the camera and target along the Y axis.
     */
    
    GAME.Camera.object.position.y += delta
    GAME.Camera.target.y += delta
    GAME.sun.target.position.y = GAME.Camera.target.y
    
    GAME.updateCamera()
    
}


GAME.fanY = function(delta) {
    /**
     * Fan to camera along the Y axis.
     *
     * Fanning moves the position of the camera on the axis without moving its target.
     */
    
    GAME.Camera.object.position.y += delta
    
    GAME.updateCamera()
    
}



GAME.shiftZ = function(delta) {
    /**
     * Shift the camera and target along the Z axis.
     */
    
    GAME.Camera.object.position.z += delta
    GAME.Camera.target.z += delta
    GAME.sun.target.position.z = GAME.Camera.target.z
    
    GAME.updateCamera()
    
}


GAME.panRight = function(delta) {
    /**
     * Fan to camera along the Y axis.
     *
     * Fanning moves the position of the camera on the axis without moving its target.
     */
    
    GAME.Camera.lon += delta
    GAME.Camera.theta = (-90 + GAME.Camera.lon) * Math.PI / 180
    
    GAME.updateCamera()
    
}


GAME.panUp = function(delta) {
    /**
     * Fan to camera along the Y axis.
     *
     * Fanning moves the position of the camera on the axis without moving its target.
     */
    
    GAME.Camera.lat -= delta
    GAME.Camera.lat = Math.max(-85, Math.min(85, GAME.Camera.lat))
    GAME.Camera.phi = (90 + GAME.Camera.lat) * Math.PI / 180
    
    GAME.updateCamera()
    
}


/**
 * Returns the GAME.Block of the specified THREE.Mesh.
 * 
 * @param {THREE.Mesh} mesh
 */
GAME.meshToBlock = function(mesh) {
    
    GAME.meshToBlockIndex = GAME.AllBlocks.length
    
    while (GAME.meshToBlockIndex--) {
        if (GAME.AllBlocks[GAME.meshToBlockIndex].mesh === mesh)
            return GAME.AllBlocks[GAME.meshToBlockIndex]
    }
    
    return null
    
}


GAME.add = function(/*GAME.Block*/block) {
    /**
     * Add a GAME.Block to the scene and physical world
     */
    
    block.rebuild()
    
    GAME.Scene.add(block.mesh)
    
    return block
    
}



GAME.remove = function(/*GAME.Block*/block) {
    /**
     * Remove a GAME.Block from the scene and physical world
     */
    
    // if in GAME.ActiveRootBlocks then remove
    if (GAME.ActiveRootBlocks.indexOf(block) > -1)
        GAME.ActiveRootBlocks.splice(GAME.ActiveRootBlocks.indexOf(block), 1) 
    
    // if has body then remove from GAME.World
    if (block.body)
        GAME.World.remove(block.body)
    
    GAME.Scene.remove(block.mesh)
    
    return block
    
}




/**
 * GAME.Block - A physical and visual world entity
 * 
 * @param {object} args
 * @constructor
 */
GAME.Block = function(args) {
    
    args = args || {}
    args.faces = args.faces || []
    
    this.name = args.name || 'BLOCK'
    this.mass = (args.mass !== undefined) ? args.mass : 0
    
    
    var i = 0,
        faces = []
    
    for (; i < args.faces.length; i++) {
        faces.push(
            new THREE.MeshPhongMaterial({
                color    : (args.faces[i].colour !== undefined) ? args.faces[i].colour : 0x888888,
                opacity  : (args.faces[i].opacity !== undefined) ? args.faces[i].opacity : 1,
                shading  : THREE.SmoothShading,
                ambient  : (args.faces[i].colour !== undefined) ? args.faces[i].colour : 0x888888,
                shininess: (args.faces[i].shininess !== undefined) ? args.faces[i].shininess : 100,
                specular : (args.faces[i].specular !== undefined) ? args.faces[i].specular : 0x222222,
                map      : args.faces[i].map || null
            })
        )
    }
    
    
    this.mesh = new THREE.Mesh(
        args.geometry || (args.faces.length > 0) ? new THREE.CubeGeometry(
                (args.width !== undefined) ? args.width : 1,
                (args.height !== undefined) ? args.height : 1,
                (args.depth !== undefined) ? args.depth : 1,
                args.faces
        ) :
        new THREE.CubeGeometry(
            (args.width !== undefined) ? args.width : 1,
            (args.height !== undefined) ? args.height : 1,
            (args.depth !== undefined) ? args.depth : 1
        ),
        new THREE.MeshPhongMaterial({
            color    : (args.colour !== undefined) ? args.colour : 0x888888,
            opacity  : (args.opacity !== undefined) ? args.opacity : 1,
            shading  : THREE.FlatShading,
            ambient  : (args.colour !== undefined) ? args.colour : 0x888888,
            shininess: (args.shininess !== undefined) ? args.shininess : 100,
            specular : (args.specular !== undefined) ? args.specular : 0x222222,
            map      : args.map || null
        })
    )
    
    this.mesh.useQuaternion = true
    this.mesh.castShadow = true
    this.mesh.receiveShadow  = true
    this.mesh.doubleSided = false
    
    this.mesh.quaternion.setFromAxisAngle((args.axis || new THREE.Vector3(0, 1, 0)), args.angle || 0)
    
    this.shape = args.shape || new CANNON.Box(new CANNON.Vec3(
        ((args.width !== undefined) ? args.width : 1) * 0.5,
        ((args.height !== undefined) ? args.height : 1) * 0.5,
        ((args.depth !== undefined) ? args.depth : 1) * 0.5
    ))
    
    this.mesh.position.x = args.x || 0
    this.mesh.position.y = args.y || 0
    this.mesh.position.z = args.z || 0
    
    this.blocks = []
    this.root = this
    
    this.worldDirection = GAME.InitialDirection.clone()
    this.mesh.quaternion.multiplyVector3(this.worldDirection)
    
    this.worldPosition = new THREE.Vector3(args.x, args.y, args.z)
    
    GAME.Meshes.push(this.mesh)
    GAME.AllBlocks.push(this)
    
}

/**
 * Add a child GAME.Block.
 * 
 * @param {GAME.Block} block
 */
GAME.Block.prototype.add = function(block) {
    
    if (block.body) {
        
        if (GAME.ActiveRootBlocks.indexOf(block) > -1)
            GAME.ActiveRootBlocks.splice(GAME.ActiveRootBlocks.indexOf(block), 1)
        
        GAME.World.remove(block.body)
        block.body = null
        block.shapes = null
        
    } else if (block.parent != null) {
        
        // detach from parent
        block.parent.blocks.splice(block.parent.blocks.indexOf(block), 1)
        
        // if a root block has shapes; detach shapes from block and rebuild
        if (block.root != null && block.root.shapes != null) {
            
            block.detachShapes()
            block.root.rebuild()
            
        }
        
    }
    
    block.mesh.position.x = block.worldPosition.x - this.worldPosition.x
    block.mesh.position.y = block.worldPosition.y - this.worldPosition.y
    block.mesh.position.z = block.worldPosition.z - this.worldPosition.z
    
    this.root.detachShapes()
    this.blocks.push(block)
    this.root.rebuild()
    
    this.mesh.add(block.mesh)
    block.parent = this
    
    return this
    
}

/**
 * Rebuilds the physical body.
 */
GAME.Block.prototype.rebuild = function() {
    
    if (GAME.ActiveRootBlocks.indexOf(this) > -1)
        GAME.ActiveRootBlocks.splice(GAME.ActiveRootBlocks.indexOf(this), 1)
    
    this.parent = null
    this.root = this
    
    if (this.body)
        GAME.World.remove(this.body)
    
    if (this.blocks.length === 0) {
        
        // no descendant blocks means no need for a compound
        this.shapes = null
        this.totalmass = this.mass
        this.body = new CANNON.RigidBody(this.totalmass, this.shape)
        
    } else {
        
        // create shapes container and attach own and all descendant block shapes
        this.shapes = new CANNON.Compound()
        this.totalmass = 0
        this.attachShapes(this)
        this.body = new CANNON.RigidBody(this.totalmass, this.shapes)
        
    }
    
    this.body.position.x = this.mesh.position.x
    this.body.position.y = this.mesh.position.y
    this.body.position.z = this.mesh.position.z
    
    this.body.quaternion.x = this.mesh.quaternion.x
    this.body.quaternion.y = this.mesh.quaternion.y
    this.body.quaternion.z = this.mesh.quaternion.z
    this.body.quaternion.w = this.mesh.quaternion.w
    
    GAME.World.add(this.body)
    
    if (this.mass !== 0)
        GAME.ActiveRootBlocks.push(this)
    
}

/**
 * Attach shapes to the specified GAME.Blocks body.
 * 
 * @param {GAME.Block} root
 */
GAME.Block.prototype.attachShapes = function(root) {
    
    this.root = root
    this.root.totalmass += this.mass
    
    root.shapes.addChild(
        this.shape, new CANNON.Vec3(
                this.worldPosition.x - root.worldPosition.x,
                this.worldPosition.y - root.worldPosition.y,
                this.worldPosition.z - root.worldPosition.z),
        new CANNON.Quaternion(this.mesh.quaternion.x, this.mesh.quaternion.y, this.mesh.quaternion.z)
    )
    
    var i = this.blocks.length
    while (i--)
        this.blocks[i].attachShapes(root)
    
}

/**
 * Detach shapes from this blocks body.
 */
GAME.Block.prototype.detachShapes = function() {
    
    if (this.root.shapes) {
        
        var i = this.root.shapes.childShapes.indexOf(this.shape)
        this.root.shapes.childShapes.splice(i, 1)
        i =  this.root.shapes.childOffsets.indexOf(this.shape)
        this.root.shapes.childOffsets.splice(i, 1)
        i =  this.root.shapes.childOrientations.indexOf(this.shape)
        this.root.shapes.childOrientations.splice(i, 1)
        
    }
    
    i = this.blocks.length
    while (i--)
        this.blocks[i].detachShapes()
    
}

/**
 * Set blocks position.
 * 
 * @param {float} x
 * @param {float} y
 * @param {float} z
 */
GAME.Block.prototype.setPosition = function(x, y, z) {
    
    this.mesh.position.x = x
    this.mesh.position.y = y
    this.mesh.position.z = z
    
    this.mesh.updateMatrix()
    this.mesh.updateMatrixWorld()
    
    this.update()
    
    if (this.body != null) {
        
        this.body.position.x = x
        this.body.position.y = y
        this.body.position.z = z
        
    }
    
}

/**
 * Apply a force offset from centre of block.
 * 
 * @param {float} fx
 * @param {float} fy
 * @param {float} fz
 * @param {float} px
 * @param {float} py
 * @param {float} pz
 */
GAME.Block.prototype.applyForce = function(fx, fy, fz, px, py, pz) {
    
    px = px - this.root.worldPosition.x
    py = py - this.root.worldPosition.y
    pz = pz - this.root.worldPosition.z
    
    this.root.body.tau.x -= (fy * pz) - (fz * py)
    this.root.body.tau.y -= (fz * px) - (fx * pz)
    this.root.body.tau.z -= (fx * py) - (fy * px)
    
    this.root.body.force.x += fx
    this.root.body.force.y += fy
    this.root.body.force.z += fz
    
}

/**
 * Apply a force offset from centre of block that only affects angular forces.
 * 
 * @param {float} fx
 * @param {float} fy
 * @param {float} fz
 * @param {float} px
 * @param {float} py
 * @param {float} pz
 */
GAME.Block.prototype.applyAngularForce = function(fx, fy, fz, px, py, pz) {
    
    px = px - this.root.worldPosition.x
    py = py - this.root.worldPosition.y
    pz = pz - this.root.worldPosition.z
    
    this.root.body.tau.x -= (fy * pz) - (fz * py)
    this.root.body.tau.y -= (fz * px) - (fx * pz)
    this.root.body.tau.z -= (fx * py) - (fy * px)
    
}

/**
 * Updates block states and applies weight force.
 */
GAME.Block.prototype.update = function() {
    
    this.mesh.matrixRotationWorld.extractRotation(this.mesh.matrixWorld)
    this.worldDirection = GAME.InitialDirection.clone()
    this.mesh.matrixRotationWorld.multiplyVector3(this.worldDirection)
    this.worldPosition = this.mesh.matrixWorld.getPosition().clone()
    
    if (this.root != this)
        this.applyAngularForce(0, -this.mass, 0, this.worldPosition.x, this.worldPosition.y, this.worldPosition.z)
    
    var i = this.blocks.length
    while (i--)
        this.blocks[i].update.call(this.blocks[i])
    
}

/**
 * Sets this as the GAME.Mouse.dragBlock if enable is true.
 * 
 * @param {boolean} enable
 */
GAME.Block.prototype.followMouse = function(enable) {
    
    GAME.Mouse.dragBlock = enable ? this : null
    
}

/**
 * Mouse begins hovering over this block.
 */
GAME.Block.prototype.onMouseEnter = GAME.EmptyCallback

/**
 * Mouse ends hovering over this block.
 */
GAME.Block.prototype.onMouseExit = GAME.EmptyCallback

/**
 * Mouse button is down whilst over this block.
 * 
 * @param {event} event
 */
GAME.Block.prototype.onMouseDown = GAME.EmptyCallback

/**
 * Mouse wheel is moved whilst over this block.
 * 
 * @param {integer} delta
 */
GAME.Block.prototype.onMouseWheel = GAME.EmptyCallback

/**
 * Mouse is moving whilst button down and over this block.
 */
GAME.Block.prototype.onMouseDrag = GAME.EmptyCallback




})
